﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.CSS
{
    #region Using Statements

    using System;
    using System.Collections.Generic;
    using KLF.Lib.Browser.DOM.CSS.SAC.Selectors;
    using System.Text;

    #endregion

    /// <summary>
    /// The CSSStyleDeclaration interface represents a single CSS declaration 
    /// block. This interface may be used to determine the style properties 
    /// currently set in a block or to set style properties explicitly 
    /// within the block.
    /// 
    /// While an implementation may not recognize all CSS properties within a 
    /// CSS declaration block, it is expected to provide access to all specified
    /// properties in the style sheet through the CSSStyleDeclaration interface. 
    /// Furthermore, implementations that support a specific level of CSS should
    /// correctly handle CSS shorthand properties for that level. For a further
    /// discussion of shorthand properties, see the CSS2Properties interface.
    /// 
    /// This interface is also used to provide a read-only access to the 
    /// computed values of an element. See also the ViewCSS interface.
    /// 
    /// Note: The CSS Object Model doesn't provide an access to the specified 
    /// or actual values of the CSS cascade.
    /// </summary>
    public class CSSStyleDeclaration
    {
        #region Constructors

        internal CSSStyleDeclaration()
        {
            Values = new Dictionary<string, CSSValue>();
        }

        #endregion

        #region Attributes

        internal Dictionary<string, CSSValue> Values { get; set; }

        #endregion

        #region Methods

        /// <summary>
        /// Add a property to the style delcaration.
        /// </summary>
        /// <param name="val"></param>
        internal void AddProperty(string propertyName, CSSValue val)
        {
            Values[propertyName] = val;
        }

        #endregion

        #region DOM Level 2 CSS Compliance

        /// <summary>
        /// The parsable textual representation of the declaration block 
        /// (excluding the surrounding curly braces). Setting this attribute 
        /// will result in the parsing of the new value and resetting of all 
        /// the properties in the declaration block including the removal or 
        /// addition of properties.
        /// </summary>
        public string cssText
        {
            get
            {
                return ToString();
            }
            set
            {
                _cssText = new StringLookup(value);
            }
        }
        internal StringLookup _cssText;

        /// <summary>
        /// Used to retrieve the object representation of the value of a CSS 
        /// property if it has been explicitly set within this declaration 
        /// block. This method returns null if the property is a shorthand 
        /// property. Shorthand property values can only be accessed and 
        /// modified as strings, using the getPropertyValue and setProperty 
        /// methods. 
        /// </summary>
        /// 
        /// <param name="propertyName">The name of the CSS property. 
        /// See the CSS property index.</param>
        /// 
        /// <returns>Returns the value of the property if it has been 
        /// explicitly set for this declaration block. Returns null if the 
        /// property has not been set.</returns>
        public string getPropertyCSSValue(string propertyName)
        {
            if (Values.ContainsKey(propertyName))
                return Values[propertyName].ToString();
            return null;
        }

        /// <summary>
        /// Used to retrieve the priority of a CSS property (e.g. the 
        /// "important" qualifier) if the property has been explicitly 
        /// set in this declaration block. 
        /// </summary>
        /// 
        /// <param name="propertyName">The name of the CSS property. 
        /// See the CSS property index.</param>
        /// 
        /// <returns></returns>
        public string getPropertyPriority(string propertyName)
        {
            if (Values.ContainsKey(propertyName))
            {
                return Values[propertyName].PropertyPriority;
            }
            return "";
        }

        /// <summary>
        /// Used to retrieve the value of a CSS property if it has been 
        /// explicitly set within this declaration block. 
        /// </summary>
        /// 
        /// <param name="propertyName">The name of the CSS property. 
        /// See the CSS property index.</param>
        /// 
        /// <returns>Returns the value of the property if it has been 
        /// explicitly set for this declaration block. Returns the empty 
        /// string if the property has not been set.</returns>
        public string getPropertyValue(string propertyName)
        {
            if (Values.ContainsKey(propertyName))
            {
                return Values[propertyName].cssText;
            }
            return String.Empty;
        }

        /// <summary>
        /// Used to retrieve the properties that have been explicitly set in
        /// this declaration block. The order of the properties retrieved 
        /// using this method does not have to be the order in which they 
        /// were set. This method can be used to iterate over all properties 
        /// in this declaration block. 
        /// </summary>
        /// <param name="index">Index of the property name to retrieve.</param>
        /// <returns>The name of the property at this ordinal position. The
        /// empty string if no property exists at this position.</returns>
        public string item(int index)
        {
            if (index < 0 || index > Values.Count)
            {
                int c = 0;

                foreach (var item in Values)
                {
                    if (c++ == index)
                        return item.Value.ToString();
                }
            }
            return String.Empty;
        }

        /// <summary>
        /// The number of properties that have been explicitly set in this
        /// declaration block. The range of valid indices is 0 to length-1
        /// inclusive.
        /// </summary>
        public int length
        {
            get
            {
                return 0;
            }
        }

        /// <summary>
        /// The CSS rule that contains this declaration block or null if this 
        /// CSSStyleDeclaration is not attached to a CSSRule.
        /// </summary>
        public CSSRule parentRule
        {
            get;
            internal set;
        }

        /// <summary>
        /// Used to remove a CSS property if it has been explicitly set within
        /// this declaration block. 
        /// </summary>
        /// 
        /// <param name="propertyName">The name of the CSS property. See the 
        /// CSS property index.</param>
        /// 
        /// <returns>Returns the value of the property if it has been 
        /// explicitly set for this declaration block. Returns the empty string
        /// if the property has not been set or the property name does not 
        /// correspond to a known CSS property.</returns>
        public string removeProperty(string propertyName)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Used to set a property value and priority within this declaration 
        /// block.
        /// </summary>
        /// <param name="propertyName">The name of the CSS property. See the 
        /// CSS property index.</param>
        /// <param name="value">The new value of the property.</param>
        /// <param name="priority">The new priority of the property 
        /// (e.g. "important").</param>
        public void setProperty(string propertyName, string value, string priority)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <param name="priority"></param>
        internal void setProperty(string propertyName, CSSValue value, string priority)
        {
            if (value == null)
                value.PropertyPriority = priority;
            Values[propertyName] = value;
        }

        #endregion

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            foreach (var item in Values)
            {
                sb.Append(item.Key);
                sb.Append(": ");
                sb.Append(item.Value.cssText);
                sb.Append(";\n");
            }

            return sb.ToString();
        }
    }
}
